#include "max7219_dot_matrix.h"

// the registers for the MAX7221 and MAX7219
#define REG_DECODE        0x09                        // "decode mode" register
#define REG_INTENSITY     0x0a                        // "intensity" register
#define REG_SCAN_LIMIT    0x0b                        // "scan limit" register
#define REG_SHUTDOWN      0x0c                        // "shutdown" register
#define REG_DISPLAY_TEST  0x0f                        // "display test" register

static u8 moduleDisp[64];
static u8 maxDevices;
static u8 spidata[16];


//------------------------------------------------------------------------------------------------------------------
// Descrição:	Escreve no módulo desejado
//------------------------------------------------------------------------------------------------------------------
static void max7219_Write (uint addr, u8 reg_number, u8 dataout) {
    //Create an array with the data to shift out
    int offset=addr*2;
    int maxbytes=maxDevices*2;
    int i; for(i=0;i<maxbytes;i++) spidata[i]=0;
    //put our device data into the array
    spidata[offset+1] = reg_number;
    spidata[offset] = dataout;

    max7219_Enable();
    for(i=maxbytes;i>0;i--)
    	max7219_TransferByte(spidata[i-1]); // write register number to MAX7219
  	max7219_Disable();
}

//------------------------------------------------------------------------------------------------------------------
// Descrição:	Inicializa pinos do controlador os drivers max7219
//				O acesso é feito via barramento SPI no modo padrão com velocidade máxima de 10MHz
// Parametros	nModule: Quantidade de módulos em cascata que deve ser gerenciado
// Retorna: 	errDEVICE_INVALID: Excedeu o número máximo de módulos que o gerenciador possa controlar
//				pdPASS: Inicialização feita com sucesso
//------------------------------------------------------------------------------------------------------------------
int max7219_Init(uint nDevices) {
	if ( (nDevices < 1) || (nDevices > 8)) return errDEVICE_INVALID;
	maxDevices = nDevices;
	max7219_SetupPorts();	// Configurar os pinos de controle

	int x; for(x=0;x<64;x++) moduleDisp[x] = 0;
	for(x=0;x<maxDevices;x++) {
		max7219_Write(x, REG_DISPLAY_TEST, 0);              // Wtihout test
		max7219_SetScanLimit(x,7);							// Scanlimit is set to max on startup
		max7219_Write(x, REG_DECODE, 0x00);                 // set to "no decode" for all digits
		max7219_Shutdown(x, pdFALSE);                       // select normal operation (i.e. not shutdown)
		max7219_DisplayTest(x, pdFALSE);                    // select normal operation (i.e. not test mode)
		max7219_Clear(x);                                   // clear all digits
		max7219_SetBrightness(x, max7219_INTENSITY_MAX);    // set to maximum intensity
	}

  	return pdPASS;
}

//------------------------------------------------------------------------------------------------------------------
// Descrição:	Scanlimit is set to max on startup
// Parametros	addr: Módulo a configurar
//				limit: Valor de configuração
// Retorna: 	errDEVICE_INVALID: O módulo não existe
//				errDEVICE_VALUE_OUT_OF_RANGE: Valor inválido
//				pdPASS: Configuração feita com sucesso
//------------------------------------------------------------------------------------------------------------------
int max7219_SetScanLimit(uint addr, uint limit) {
    if (addr>=maxDevices) return errDEVICE_INVALID;
    if (limit>7) return errDEVICE_VALUE_OUT_OF_RANGE;
   	max7219_Write(addr, REG_SCAN_LIMIT, limit);
    return pdPASS;
}

//------------------------------------------------------------------------------------------------------------------
// Descrição:	Liga ou desliga o módulo
// Parametros	addr: Módulo a configurar
//				st:
//					pdON: Liga módulo
//					pdOFF: Desliga módulo
// Retorna: 	errDEVICE_INVALID: O módulo não existe
//				pdPASS: Configuração feita com sucesso
//------------------------------------------------------------------------------------------------------------------
int max7219_Shutdown(uint addr, u8 st) {
	if (addr>=maxDevices) return errDEVICE_INVALID;
	if (st) max7219_Write(addr, REG_SHUTDOWN, 0);	// put MAX7219 into "shutdown" mode
	else max7219_Write(addr, REG_SHUTDOWN, 1);  	// put MAX7219 into "normal" mode
	return pdPASS;
}

//------------------------------------------------------------------------------------------------------------------
// Descrição:	Inicia o procedimento de teste no display
// Parametros	addr: Módulo a configurar
//				st:
//					pdON: Inicia o procedimento de auto teste
//					pdOFF: Desliga o procedimento de auto teste
// Retorna: 	errDEVICE_INVALID: O módulo não existe
//				pdPASS: Configuração feita com sucesso
//------------------------------------------------------------------------------------------------------------------
int max7219_DisplayTest(uint addr, u8 st) {
  	if (addr>=maxDevices) return errDEVICE_INVALID;
  	if (st > 1) st = 1;
  	max7219_Write(addr, REG_DISPLAY_TEST, st);                 // put MAX7219 into "display test" mode
  	return pdPASS;
}

//------------------------------------------------------------------------------------------------------------------
// Descrição:	Ajusta a itensidade do display
// Parametros	addr: Módulo a configurar
//				brightness: Valor do brilho entre 0 a 15
// Retorna: 	errDEVICE_INVALID: O módulo não existe
//				pdPASS: Configuração feita com sucesso
//------------------------------------------------------------------------------------------------------------------
int max7219_SetBrightness(uint addr, u8 brightness) {
  	if (addr>=maxDevices) return errDEVICE_INVALID;
  	brightness &= 0x0f;                                 // mask off extra bits
  	max7219_Write(addr, REG_INTENSITY, brightness);           // set brightness
  	return pdPASS;
}

//------------------------------------------------------------------------------------------------------------------
// Descrição:	Limpa o módulo
// Parametros	addr: Módulo a ser acessado
// Retorna: 	errDEVICE_INVALID: O módulo não existe
//				pdPASS: Acesso feito com sucesso
//------------------------------------------------------------------------------------------------------------------
int max7219_Clear(uint addr) {
	if (addr>=maxDevices) return errDEVICE_INVALID;
	int offset=addr*8;
  	u8 i; for (i=0; i<8; i++) {
   		moduleDisp[offset+i]=0;
        max7219_Write(addr, i+1,moduleDisp[offset+i]);
    }
  	return pdPASS;
}

//------------------------------------------------------------------------------------------------------------------
// Descrição:	Liga ou desliga um determinado led da matrix
// Parametros	addr: Módulo a ser acessado
// 				row, columm: Linha e coluna
//				st:
//					pdON: Liga o led
//					pdOFF: Desliga o led
// Retorna: 	errDEVICE_INVALID: O módulo, ou digito, não existe
//				pdPASS: Acesso feito com sucesso
//------------------------------------------------------------------------------------------------------------------
int max7219_SetLed(uint addr, uint row, uint column, int st) {
    if(addr>=maxDevices) return errDEVICE_INVALID;
    if((row>7) || (column>7)) return errDEVICE_INVALID;

    int offset;
    u8 val=0x00;

    offset=addr*8;
    val= 0x80 >> column;
    if (st) moduleDisp[offset+row]=moduleDisp[offset+row]|val;
    else {
        val=~val;
        moduleDisp[offset+row]=moduleDisp[offset+row]&val;
    }
    max7219_Write(addr, row+1,moduleDisp[offset+row]);
    return pdPASS;
}

//------------------------------------------------------------------------------------------------------------------
// Descrição:	Liga ou desliga leds de uma determinada linha da matrix
// Parametros	addr: Módulo a ser acessado
// 				row: Linha a ser acessada
//				value: valor da linha
// Retorna: 	errDEVICE_INVALID: O módulo, ou digito, não existe
//				pdPASS: Acesso feito com sucesso
//------------------------------------------------------------------------------------------------------------------
int max7219_SetRow(uint addr, uint row, u8 value) {
    if (addr>=maxDevices) return errDEVICE_INVALID;
    if (row>7) return errDEVICE_INVALID;

    int offset;
    offset=addr*8;
    moduleDisp[offset+row]=value;
    max7219_Write(addr, row+1,moduleDisp[offset+row]);

    return pdPASS;
}

//------------------------------------------------------------------------------------------------------------------
// Descrição:	Liga ou desliga leds de uma determinada coluna da matrix
// Parametros	addr: Módulo a ser acessado
// 				col: Coluna a ser acessada
//				value: valor da coluna
// Retorna: 	errDEVICE_INVALID: O módulo, ou digito, não existe
//				pdPASS: Acesso feito com sucesso
//------------------------------------------------------------------------------------------------------------------
int max7219_SetColumn(uint addr, uint col, u8 value) {
    if (addr>=maxDevices) return errDEVICE_INVALID;
    if (col>7) return errDEVICE_VALUE_OUT_OF_RANGE;

    u8 val;
    uint row; for(row=0;row<8;row++) {
        val=value >> (7-row);
        val=val & 0x01;
        max7219_SetLed(addr, row, col, val);
    }

   return pdPASS;
}

#include "font8x8_basic.h"
// inverte bits
static u8 _inv(const u8 value) {
	u8 res=0;
	int i; for (i=0;i<8;i++) {
		if (value&_BV(i)) res|=(_BV(7-i));
	}
	return res;
}

//------------------------------------------------------------------------------------------------------------------
// Descrição:	Imprime um caractere em um determinado módulo
// Parametros	addr: Módulo a ser acessado
//				value: Valor ASCII a ser impresso
// Retorna: 	errDEVICE_INVALID: O módulo, ou digito, não existe
//				errDEVICE_VALUE_OUT_OF_RANGE: Valor ASCII não existe
//				pdPASS: Acesso feito com sucesso
//------------------------------------------------------------------------------------------------------------------
int max7219_SetChar(int addr, u8 value) {
    if (addr>=maxDevices) return errDEVICE_INVALID;
    if (value>127) return errDEVICE_VALUE_OUT_OF_RANGE;
    int offset=addr*8;
	u8 v;
	int digit; for (digit=0;digit<8;digit++) {
		v = _inv(font8x8_basic[value][digit]);
    	moduleDisp[offset+digit]=v;
      	max7219_Write(addr, digit+1, v);
	}
    return pdPASS;
}
